﻿using StarryEdge.Core.Device;
using StarryEdge.Core.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StarryEdge.Core;
using StarryEdge.Parameter;
using StarryEdge.Devices.Models;
using StarryEdge.Core.Enums;
using System.Reflection;
using StarryEdge.Device.Connections;
using StarryEdge.Devices.MeterDevice.Modbus;
using StarryEdge.Devices.Protocol.Modbus;
using StarryEdge.Core.Parameter;
using System.Diagnostics.CodeAnalysis;
using StarryEdge.Devices.MeterDevice.ScanCode;
using StarryEdge.Devices.MeterDevice.ElectronicScales;
using StarryEdge.Devices.MeterDevice.Testers;
using StarryEdge.Core.Models.Testers;
using StarryEdge.Devices.MeterDevice;

namespace StarryEdge.Devices
{
    public class DeviceFactory : IDeviceFactory
    {
        #region 私有成员
        private IStarryEdgeLog _edgeLog;
        private IDialogHost _dialogHost;
        private ILangProvider _langProvider;
        private IDeviceInfoManage _deviceInfo;
        private List<DeviceConnectionModel> _connectionModels;
        private CancellationTokenSource _tokenSource;
        private SystemConfigParameterModel _systemConfig;
        #endregion

        public DeviceFactory(IDeviceInfoManage deviceInfo, IStarryEdgeLog edgeLog, IDialogHost dialogHost, ILangProvider langProvider, ISystemConfigParameter systemConfig)
        {
            _systemConfig = systemConfig.GetParameter();
            _deviceInfo = deviceInfo;
            _dialogHost = dialogHost;
            _langProvider = langProvider;
            _edgeLog = edgeLog;
            _connectionModels = new List<DeviceConnectionModel>();
            _tokenSource = new CancellationTokenSource();
        }

        public async Task<bool> Connect()
        {
            if (_connectionModels.Count > 0)
            {
                return true;
            }
            if (_tokenSource.Token.IsCancellationRequested)
            {
                _tokenSource = new CancellationTokenSource();
            }
            foreach (var item in _deviceInfo.GetDevices())
            {
                _connectionModels.Add(new DeviceConnectionModel()
                {
                    Info = item,
                });
            }
            bool result = await Task.Run(() => Parallel.ForEach(_connectionModels, (x, _parallelLoopState) =>
            {
                if (x.Info.Index == 0 || string.IsNullOrEmpty(x.Info.IPCOM) || x.Info.Index == 999) { return; }
                ICommunication com = null;
                if (x.Info.IsSocket)
                {
                    if (x.Info.IsUDP)
                    {
                        com = new UDPSocket(_systemConfig.FinsIP, x.Info);
                    }
                    else
                    {
                        com = new TCPSocket(_systemConfig.FinsIP, x.Info);
                    }
                }
                else
                {
                    com = new SerialPortFunc(x.Info);
                }
                IDevice device = GetDevice(com, x.Info);
                if (device == null)
                {
                    _parallelLoopState.Stop();
                    _edgeLog.LogRun("No Device", LogLevel.Error);
                    return;
                }
                bool? bools = device.Open();
                if (bools != null && bools == true)
                {
                    x.Device = device;
                }
                else
                {
                    _parallelLoopState.Stop();
                    StringBuilder stringBuilder = new StringBuilder();
                    stringBuilder.Append(string.Format("{0}:[{1}] ", _langProvider.GetLang("Device"), x.Info.DeviceType));
                    stringBuilder.Append(string.Format("{0}:[{1}] ", _langProvider.GetLang("Index"), x.Info.Index));
                    stringBuilder.Append(string.Format("{0}:[{1}] ", "IP", x.Info.IPCOM));
                    stringBuilder.Append(string.Format("{0}:[{1}] ", "Port", x.Info.Port));
                    stringBuilder.Append(_langProvider.GetToolTip("LinkFailure"));
                    _edgeLog.LogRun(stringBuilder.ToString(), LogLevel.Error);
                }
            }).IsCompleted);
            if (result)
            {
                return true;
            }
            _connectionModels.Clear();
            return false;
        }

        private IDevice GetDevice(ICommunication com, DeviceInfoModel deviceInfo)
        {
            switch (deviceInfo.DeviceType)
            {
                case DeviceTypes.Int_NoProtocol:
                    return new NoProtocolInt(com, _edgeLog, Reconnection);
                case DeviceTypes.String_NoProtocol:
                    return new NoProtocolString(com, _edgeLog, Reconnection);
                case DeviceTypes.Modbus_TCP_ABCD:
                    return new Modbus(deviceInfo.StationNumber, com, _edgeLog, EndianTypes.Modbus_ABCD, new TCP_Modbus_Protocol(deviceInfo.StationNumber), Reconnection);
                case DeviceTypes.Modbus_TCP_BADC:
                    return new Modbus(deviceInfo.StationNumber, com, _edgeLog, EndianTypes.Modbus_BADC, new TCP_Modbus_Protocol(deviceInfo.StationNumber), Reconnection);
                case DeviceTypes.Modbus_TCP_DCBA:
                    return new Modbus(deviceInfo.StationNumber, com, _edgeLog, EndianTypes.Modbus_DCBA, new TCP_Modbus_Protocol(deviceInfo.StationNumber), Reconnection);
                case DeviceTypes.Modbus_TCP_CDAB:
                    return new Modbus(deviceInfo.StationNumber, com, _edgeLog, EndianTypes.Modbus_CDAB, new TCP_Modbus_Protocol(deviceInfo.StationNumber), Reconnection);
                case DeviceTypes.Modbus_RTU_ABCD:
                    return new Modbus(deviceInfo.StationNumber, com, _edgeLog, EndianTypes.Modbus_ABCD, new RTU_Modbus_Protocol(deviceInfo.StationNumber), Reconnection);
                case DeviceTypes.Modbus_RTU_BADC:
                    return new Modbus(deviceInfo.StationNumber, com, _edgeLog, EndianTypes.Modbus_BADC, new RTU_Modbus_Protocol(deviceInfo.StationNumber), Reconnection);
                case DeviceTypes.Modbus_RTU_DCBA:
                    return new Modbus(deviceInfo.StationNumber, com, _edgeLog, EndianTypes.Modbus_DCBA, new RTU_Modbus_Protocol(deviceInfo.StationNumber), Reconnection);
                case DeviceTypes.Modbus_RTU_CDAB:
                    return new Modbus(deviceInfo.StationNumber, com, _edgeLog, EndianTypes.Modbus_CDAB, new RTU_Modbus_Protocol(deviceInfo.StationNumber), Reconnection);
            }
            throw new Exception("未实现该设备类型功能");
        }

        public void Disconnect()
        {
            try
            {
                foreach (var connectionModel in _connectionModels)
                {
                    if (connectionModel.Info.Index != 999 || connectionModel.Device != null)
                    {
                        connectionModel.Device?.Close();
                    }
                }
                _connectionModels.Clear();
            }
            catch (Exception ex)
            {
                _dialogHost.DialogShow($"Device Close Exception：{ex}", Core.Enums.LogLevel.Warning, true, hostDialog: Core.Enums.PopUpTypes.Global);
            }
        }

        public T GetDevice<T>(Func<DeviceInfoModel, bool> func)
        {
            var model = _connectionModels.FirstOrDefault(x => func(x.Info));
            if (model != null && model.Device is T t)
            {
                return t;
            }
            return default;
        }

        private bool Reconnection(DeviceInfoModel model, IDevice device)
        {
            try
            {
                if (_tokenSource.Token.IsCancellationRequested)
                {
                    return false;
                }
                device.Close();
                Thread.Sleep(350);
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.Append(string.Format("{0}:[{1}] ", _langProvider.GetLang("Device"), model.DeviceType));
                stringBuilder.Append(string.Format("{0}:[{1}] ", _langProvider.GetLang("Index"), model.Index));
                stringBuilder.Append(string.Format("{0}:[{1}] ", "IP", model.IPCOM));
                stringBuilder.Append(string.Format("{0}:[{1}] ", "Port", model.Port));
                if (device.Open())
                {
                    stringBuilder.Append(string.Format("----重连{0}", _langProvider.GetLang("Success")));
                    _edgeLog.LogRun(stringBuilder.ToString(), LogLevel.Success);
                    return true;
                }
                stringBuilder.Append(string.Format("----重连{0}", _langProvider.GetLang("Lose")));
                _edgeLog.LogRun(stringBuilder.ToString(), LogLevel.Warning);
                Thread.Sleep(350);
                return true;
            }
            catch (Exception ex)
            {
                Thread.Sleep(500);
                _edgeLog.LogRun($"Device Reconnection Exception：{ex}", Core.Enums.LogLevel.Error);
            }
            return false;
        }

        public IDevice GetDeviceObject(DeviceInfoModel x)
        {
            ICommunication com;
            if (x.IsSocket)
            {
                if (x.IsUDP)
                {
                    com = new UDPSocket(_systemConfig.FinsIP, x);
                }
                else
                {
                    com = new TCPSocket(_systemConfig.FinsIP, x);
                }
            }
            else
            {
                com = new SerialPortFunc(x);
            }
            IDevice device = GetDevice(com, x);
            return device;
        }
    }
}
